package com.loveprogrammer.eternity.model.game;

import com.loveprogrammer.eternity.model.BossKill;
import com.loveprogrammer.eternity.model.Hero;
import com.loveprogrammer.eternity.model.Monster;
import com.loveprogrammer.eternity.model.console.ConsolePrint;
import com.loveprogrammer.eternity.model.map.DefaultFindMonsters;
import com.loveprogrammer.eternity.model.map.FindMonstersEngine;
import com.loveprogrammer.eternity.model.map.Map;
import com.loveprogrammer.eternity.model.mission.MissionReward;
import com.loveprogrammer.eternity.model.mission.RewardProperties;
import com.loveprogrammer.eternity.model.product.Coin;
import com.loveprogrammer.eternity.model.product.Equipment;
import com.loveprogrammer.eternity.model.product.Goods;
import com.loveprogrammer.eternity.model.product.Things;
import com.loveprogrammer.eternity.model.product.equipment.Equips;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @version 1.0.0
 * @description:
 * @author: eric
 * @date: 2022-08-05 16:17
 **/
public class HeroWrapper implements Serializable {

    /**
     * 最多孵化20个宠物 出战5个宠物
     **/
    private static final int MAX_PET_NUM = 20;

    private Hero hero;

    private FindMonstersEngine findMonsterEngine;

    // 挂机地图
    private Map hangUpMap;

    // 仓库 或者 背包
    private Warehouse warehouse;

    // 宠物
    private List<PetWrapper> pets;

    private List<Monster> fightPets;

    // 上次下线时间
    private Long offlineTime;

    // 总在线时长
    private Long totalPlayTime;

    // 别名
    private String aliasName;

    // 一些基本的设定
    private GameSettings settings;

    // 游戏状态保存
    private GameStatus gameStatus;

    // 游戏任务列表
    private TaskManager taskManager;

    // 击杀过的boss
    private java.util.Map<Integer,BossKill> killHistory;

    public HeroWrapper() {
    }

    public HeroWrapper(Hero hero, String aliasName) {
        this.hero = hero;
        this.aliasName = aliasName;
        this.findMonsterEngine = new DefaultFindMonsters();
        this.warehouse = new Warehouse();
        this.pets = new ArrayList<>();
        this.fightPets = new ArrayList<>();
        this.offlineTime = System.currentTimeMillis();
        this.totalPlayTime = 0L;
        this.settings = new GameSettings();
        this.gameStatus = new GameStatus();
    }

    public HeroWrapper(Hero hero, FindMonstersEngine findMonsterEngine, Map hangUpMap,
                       Warehouse warehouse, List<PetWrapper> pets, List<Monster> fightPets,
                       Long offlineTime, Long totalPlayTime, String aliasName, GameSettings settings,
                       GameStatus gameStatus) {
        this.hero = hero;
        this.findMonsterEngine = findMonsterEngine;
        this.hangUpMap = hangUpMap;
        this.warehouse = warehouse;
        this.pets = pets;
        this.fightPets = fightPets;
        this.offlineTime = offlineTime;
        this.totalPlayTime = totalPlayTime;
        this.aliasName = aliasName;
        this.settings = settings;
        this.gameStatus = gameStatus;
    }

    public Hero getHero() {
        return hero;
    }

    public void setHero(Hero hero) {
        this.hero = hero;
    }

    public Map getHangUpMap() {
        return hangUpMap;
    }

    public void setHangUpMap(Map hangUpMap) {
        this.hangUpMap = hangUpMap;
    }

    public Warehouse getWarehouse() {
        return warehouse;
    }

    public void setWarehouse(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    public Long getOfflineTime() {
        return offlineTime;
    }

    public void setOfflineTime(Long offlineTime) {
        this.offlineTime = offlineTime;
    }

    public Long getTotalPlayTime() {
        return totalPlayTime;
    }

    public void setTotalPlayTime(Long totalPlayTime) {
        this.totalPlayTime = totalPlayTime;
    }

    public String getAliasName() {
        return aliasName;
    }

    public void setAliasName(String aliasName) {
        this.aliasName = aliasName;
    }

    public FindMonstersEngine getFindMonsterEngine() {
        return findMonsterEngine;
    }

    public void setFindMonsterEngine(FindMonstersEngine findMonsterEngine) {
        this.findMonsterEngine = findMonsterEngine;
    }

    public List<PetWrapper> getPets() {
        return pets;
    }

    public void setPets(List<PetWrapper> pets) {
        this.pets = pets;
    }

    public List<Monster> getFightPets() {
        return fightPets;
    }

    public void setFightPets(List<Monster> fightPets) {
        this.fightPets = fightPets;
    }

    // 拾取装备
    public void pickUp(List<Goods> fallingGoods, Coin fallingCoin) {
        if(fallingGoods != null) {
            warehouse.pickUp(fallingGoods);
        }
        if(fallingCoin != null) {
            warehouse.pickUp(fallingCoin);
        }
    }

    public void addExp(int fallingExp,int monsterLevel) {
        // 找到当前职业的最高等级
        int highestLevel = getHero().getTemplate().getHighestLevel();

        int total = fightPets.size() + 1;
        // 经验要分一分
        fallingExp = (int) ((1.1 - total/10) * fallingExp);

        // 更新出战宠物的经验
        for (Monster fightPet : fightPets) {
            int upExp = fallingExp;
            if(fightPet.getLevel() > (monsterLevel + 5)){
                upExp = 1;
            }else if(fightPet.getLevel() > (monsterLevel + 2)){
                upExp = (int) (fallingExp * 0.5);
            }

            Integer currentExp = fightPet.getCurrentExp();
            int exp = currentExp + upExp;
            fightPet.setCurrentExp(exp);
            if(fightPet.getNextLevelNeedExp() < exp){
                // 升级了
                ConsolePrint.publishMessage("战宠" + fightPet.getName() + "升级了");
                fightPet.levelUp(exp);
            }
        }

        if(getHero().getLevel() >= highestLevel){
            ConsolePrint.publishMessage("已经达到当前职业模板的最高级，请转职",1);
            return;
        }
        // 更新英雄的经验
        int upExp = fallingExp;
        if(hero.getLevel() > (monsterLevel + 5)){
            upExp = 1;
        }else if(hero.getLevel() > (monsterLevel + 2)){
            upExp = (int) (fallingExp * 0.5);
        }

        Integer currentExp = this.hero.getCurrentExp();
        int exp = currentExp + upExp;
        this.hero.setCurrentExp(exp);
        if(this.hero.getNextLevelNeedExp() < exp){
            // 升级了
            ConsolePrint.publishMessage("升级了");
            hero.levelUp(exp);
        }
    }

    public GameSettings getSettings() {
        return settings;
    }

    public void setSettings(GameSettings settings) {
        this.settings = settings;
    }

    public GameStatus getGameStatus() {
        return gameStatus;
    }

    public void setGameStatus(GameStatus gameStatus) {
        this.gameStatus = gameStatus;
    }


    public boolean buy(Goods goods) {
        Coin price = goods.price();
        Warehouse warehouse = getWarehouse();
        if(price.getDiamonds() > 0 && warehouse.getDiamonds() > price.getDiamonds()){
            warehouse.setDiamonds(warehouse.getDiamonds() - price.getDiamonds());
            getGoods(goods);
            return true;
        }else if(price.getGoldCoin() > 0
                && (warehouse.getGoldCoin() > price.getGoldCoin() || warehouse.getDiamonds() * 10 > price.getGoldCoin() )){
            warehouse.setDiamonds(warehouse.getDiamonds() - price.getDiamonds());
            getGoods(goods);
            return true;
        }


        else{
            return false;
        }
    }

    private void getGoods(Goods goods) {
        if(goods instanceof Equipment){
            Equipment equipment = (Equipment) goods;
            getWarehouse().getEquipmentList().add(equipment);
        }else{
            Things things = (Things) goods;
            getWarehouse().pickUp(Arrays.asList(things));
        }
    }

    public TaskManager getTaskManager() {
        if(taskManager == null){
            taskManager = new TaskManager();
        }
        return taskManager;
    }

    public void setTaskManager(TaskManager taskManager) {
        this.taskManager = taskManager;
    }

    public void takeOff(String takeOffId) {
        Equipment equipment = getWarehouse().getEquipmentList().stream().filter(e -> e.getId().equals(takeOffId)).findFirst().orElse(null);
        if(equipment != null){
            equipment.setUsed(false);
        }
    }

    public void resetThings() {
        Equips equips = hero.getEquips();
        List<Equipment> equipments = equips.getAll();
        List<String> ids = equipments.stream().map(Equipment::getId).collect(Collectors.toList());

        List<Equipment> equipmentList = getWarehouse().getEquipmentList();
        for (Equipment equipment : equipmentList) {
            if(ids.contains(equipment.getId())){
                continue;
            }
            equipment.setUsed(false);
        }
    }

    // 领取奖励
    public void rewordGoods(MissionReward reward) {

        java.util.Map<Goods, Integer> rewards = reward.getRewards(this.hero);
        Iterator<java.util.Map.Entry<Goods, Integer>> iterator = rewards.entrySet().iterator();
        while (iterator.hasNext()){
            java.util.Map.Entry<Goods, Integer> entry = iterator.next();
            Goods goods = entry.getKey();
            Integer size = entry.getValue();
            List<Goods> rewardGoods = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                rewardGoods.add(goods);
            }

            warehouse.pickUp(rewardGoods);
        }
        Coin fallingCoin = new Coin();
        fallingCoin.setGoldCoin(Optional.of(reward.getGoldCoin()).orElse(0));
        fallingCoin.setDiamonds(Optional.of(reward.getDiamonds()).orElse(0));
        warehouse.pickUp(fallingCoin);

        // 经验
        int upExp = reward.getExp();
        ConsolePrint.publishMessage("获得经验【" + upExp + "】点");
        Integer currentExp = this.hero.getCurrentExp();
        int exp = currentExp + upExp;
        this.hero.setCurrentExp(exp);
        if(this.hero.getNextLevelNeedExp() < exp){
            // 升级了
            ConsolePrint.publishMessage("升级了");
            hero.levelUp(exp);
        }

        // 属性
        RewardProperties properties = reward.getProperties();
        if(properties != null){
            StringBuilder builder = new StringBuilder();
            if(properties.getStrength() > 0){
                // 属性增加
                hero.setStrength(hero.getStrength() + properties.getStrength());
                // 基础属性增加
                hero.setBaseStrength(hero.getBaseStrength() + properties.getStrength());
                builder.append("增加基础力量").append(properties.getStrength()).append("点;\n");
            }
            if(properties.getMagic() > 0){
                // 属性增加
                hero.setMagic(hero.getMagic() + properties.getMagic());
                // 基础属性增加
                hero.setBaseMagic(hero.getBaseMagic() + properties.getMagic());
                builder.append("增加基础法力").append(properties.getMagic()).append("点;\n");
            }
            if(properties.getConstitution() > 0){
                // 属性增加
                hero.setConstitution(hero.getConstitution() + properties.getConstitution());
                // 基础属性增加
                hero.setBaseConstitution(hero.getBaseConstitution() + properties.getConstitution());

                hero.getHp().addAndGet(10 * properties.getConstitution());
                hero.setHpMax(hero.getHp().get());
                builder.append("增加基础生命值").append(10 * properties.getConstitution()).append("点;\n");
            }
            if(properties.getFlameResistance() > 0){
                // 抗性增加
                hero.setFlameResistance(hero.getFlameResistance() + properties.getFlameResistance());
                builder.append("增加火抗").append(properties.getFlameResistance()).append("点;\n");
            }
            if(properties.getPoisonResistance() > 0){
                // 抗性增加
                hero.setPoisonResistance(hero.getPoisonResistance() + properties.getPoisonResistance());
                builder.append("增加毒抗").append(properties.getPoisonResistance()).append("点;\n");
            }
            if(properties.getThunderResistance() > 0){
                // 抗性增加
                hero.setThunderResistance(hero.getThunderResistance() + properties.getThunderResistance());
                builder.append("增加雷抗").append(properties.getThunderResistance()).append("点;\n");
            }
            if(properties.getIceResistance() > 0){
                // 抗性增加
                hero.setIceResistance(hero.getIceResistance() + properties.getIceResistance());
                builder.append("增加冰抗").append(properties.getIceResistance()).append("点;\n");
            }
            if(properties.getBetterEquipmentRateAdd() > 0){
                // 增加
                hero.setBetterEquipmentRateAdd(hero.getBetterEquipmentRateAdd() + properties.getBetterEquipmentRateAdd());
                builder.append("增加更好的几率获得魔法装备").append(properties.getBetterEquipmentRateAdd()).append("点;\n");
            }
            if(properties.getCriticalStrikeRateAdd() > 0){
                // 增加
                hero.setCriticalStrikeRateAdd(hero.getCriticalStrikeRateAdd() + properties.getCriticalStrikeRateAdd());
                builder.append("增加暴击率").append(properties.getCriticalStrikeRateAdd()).append("点;\n");
            }
            if(properties.getDodgeChanceAdd() > 0){
                // 增加
                hero.setDodgeChanceAdd(hero.getDodgeChanceAdd() + properties.getDodgeChanceAdd());
                builder.append("增加闪避率").append(properties.getDodgeChanceAdd()).append("点;\n");
            }
            ConsolePrint.publishMessage(builder.toString());
        }
    }

    public java.util.Map<Integer, BossKill> getKillHistory() {
        return killHistory;
    }

    public void setKillHistory(java.util.Map<Integer, BossKill> killHistory) {
        this.killHistory = killHistory;
    }

    public void checkFirstKill(int monsterId) {

    }

    public void writeFightRecords(List<Monster> monsters, Boolean result) {
        if(result == null) {
            // 平局 算了
            return;
        }
        // 胜利
        if(result) {
            GameStatus status = getGameStatus();
            status.setKillTotal(status.getKillTotal() + monsters.size());

            Set<String> ids = monsters.stream().map(Monster::getId).map(Object::toString).collect(Collectors.toSet());
            Set<String> killBossIds = status.getKillBossIds();
            if(killBossIds.containsAll(ids)) {
                return;
            }
            killBossIds.addAll(ids);
        }
    }
}
